﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;

namespace Components.Analytics
{
    [DataContract]
    public class DocumentClusteringOutput : ResultBase
    {
        #region props
        [DataMember]
        public string TaskName { get; set; }
        [DataMember]
        public Dictionary<int, string> DocTitles { get; set; }
        [DataMember]
        public Dictionary<int, string> ClusterLabels { get; set; }
        [DataMember]
        public Dictionary<int, List<int>> ClusteredDocs { get; set; }
        [DataMember]
        public Dictionary<int, double> ClusterDiameters { get; set; }
        [DataMember]
        public Dictionary<int, Dictionary<int, double>> DocDocDistances { get; set; }
        [DataMember]
        public Dictionary<int, string> AllTerms { get; set; }
        [DataMember]
        public Dictionary<int, Dictionary<int, int>> DocTermFreqs { get; set; }
        [DataMember]
        public Dictionary<int, double> TermWeights { get; set; }
        [DataMember]
        public Dictionary<int, Dictionary<int, double>> TermTermDistances { get; set; }
        #endregion

        #region ctor
        public DocumentClusteringOutput()
            : base(Guid.Empty, Guid.Empty)
        {
            this.TaskName = string.Empty;
            this.ClusterLabels = new Dictionary<int, string>();
            this.ClusteredDocs = new Dictionary<int, List<int>>();
            this.ClusterDiameters = new Dictionary<int, double>();
            this.DocDocDistances = new Dictionary<int, Dictionary<int, double>>();
            this.AllTerms = new Dictionary<int, string>();
            this.DocTermFreqs = new Dictionary<int, Dictionary<int, int>>();
            this.TermWeights = new Dictionary<int, double>();
            this.DocTitles = new Dictionary<int, string>();
            this.TermTermDistances = new Dictionary<int, Dictionary<int, double>>();
        }

        public DocumentClusteringOutput(Guid componentID, Guid ticketID,
            string taskName,
            Dictionary<int, string> clusterLabels,
            Dictionary<int, List<int>> clusteredDocs,
            Dictionary<int, double> clusterDiamters,
            Dictionary<int, Dictionary<int, double>> docDocDistances,
            Dictionary<int, string> allTerms,
            Dictionary<int, Dictionary<int, int>> docTermFreqs,
            Dictionary<int, double> termWeights,
            Dictionary<int, string> docTitles,
            Dictionary<int, Dictionary<int, double>> termTermDistances)
            : base(componentID, ticketID)
        {
            this.TaskName = taskName;
            this.ClusterLabels = clusterLabels;
            this.ClusteredDocs = clusteredDocs;
            this.ClusterDiameters = clusterDiamters;
            this.DocDocDistances = docDocDistances;
            this.AllTerms = allTerms;
            this.DocTermFreqs = docTermFreqs;
            this.TermWeights = termWeights;
            this.DocTitles = docTitles;
            this.TermTermDistances = termTermDistances;
        }
        #endregion

        #region Overrides of ResultBase

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override XElement Serialize()
        {
            XElement xe = base.Serialize();
            xe.Add(new XAttribute("TaskName", this.TaskName));
            if (this.ClusterLabels != null && this.ClusterLabels.Count > 0)
            {
                XElement clusterLabelRoot = new XElement("ClusterLabels");
                xe.Add(clusterLabelRoot);
                foreach (int clusterID in this.ClusterLabels.Keys)
                {
                    clusterLabelRoot.Add(new XElement(
                        "ClusterLabel",
                        new XAttribute("ClusterID", clusterID.ToString()),
                        new XAttribute("Label", this.ClusterLabels[clusterID])));
                }
            }
            if (this.ClusteredDocs != null && this.ClusteredDocs.Count > 0)
            {
                XElement clusteredDocRoot = new XElement("ClusteredDocs");
                xe.Add(clusteredDocRoot);
                foreach (int clusterID in this.ClusteredDocs.Keys)
                {
                    XElement clusteredDoc = new XElement("ClusteredDoc",
                        new XAttribute("ClusterID", clusterID.ToString()));
                    clusteredDocRoot.Add(clusteredDoc);
                    foreach (int docID in this.ClusteredDocs[clusterID])
                    {
                        clusteredDoc.Add(new XElement(
                            "Doc",
                            new XAttribute("DocID", docID.ToString())));
                    }
                }
            }
            if (this.ClusterDiameters != null && this.ClusterDiameters.Count > 0)
            {
                XElement clusterDiameters = new XElement("ClusterDiameters");
                xe.Add(clusterDiameters);
                foreach (int clusterID in this.ClusterDiameters.Keys)
                {
                    clusterDiameters.Add(new XElement(
                        "ClusterDiameter",
                        new XAttribute("ClusterID", clusterID.ToString()),
                        new XAttribute("Diameter", this.ClusterDiameters[clusterID].ToString())));
                }
            }
            if (this.DocDocDistances != null && this.DocDocDistances.Count > 0)
            {
                XElement docDocDistances = new XElement("DocDocDistances");
                xe.Add(docDocDistances);
                foreach (int fromDocID in this.DocDocDistances.Keys)
                {
                    foreach (int toDocID in this.DocDocDistances[fromDocID].Keys)
                    {
                        docDocDistances.Add(new XElement(
                            "DocDocDistance",
                            new XAttribute("FromDocID", fromDocID.ToString()),
                            new XAttribute("ToDocID", toDocID.ToString()),
                            new XAttribute("Distance", this.DocDocDistances[fromDocID][toDocID].ToString())));
                    }
                }
            }
            if (this.AllTerms != null && this.AllTerms.Count > 0)
            {
                XElement allTerms = new XElement("AllTerms");
                xe.Add(allTerms);
                foreach (int termID in this.AllTerms.Keys)
                {
                    allTerms.Add(new XElement(
                                     "Term",
                                     new XAttribute("TermID", termID.ToString()),
                                     new XAttribute("Phrase", this.AllTerms[termID])));
                }
            }
            if (this.DocTermFreqs != null && this.DocTermFreqs.Count > 0)
            {
                XElement docTerms = new XElement("DocTerms");
                xe.Add(docTerms);
                foreach (int docID in this.DocTermFreqs.Keys)
                {
                    XElement doc = new XElement("Doc", new XAttribute("DocID", docID.ToString()));
                    docTerms.Add(doc);
                    foreach (int termID in this.DocTermFreqs[docID].Keys)
                    {
                        doc.Add(new XElement(
                            "DocTerm",
                            new XAttribute("TermID", termID),
                            new XAttribute("TermFreq", this.DocTermFreqs[docID][termID].ToString())));
                    }
                }
            }
            if (this.TermWeights != null && this.TermWeights.Count > 0)
            {
                XElement termWeights = new XElement("TermWeights");
                xe.Add(termWeights);
                foreach (int termID in this.TermWeights.Keys)
                {
                    termWeights.Add(new XElement(
                        "TermWeight",
                        new XAttribute("TermID", termID.ToString()),
                        new XAttribute("Weight", this.TermWeights[termID].ToString())));
                }
            }
            if (this.DocTitles != null && this.DocTitles.Count > 0)
            {
                XElement titles = new XElement("Titles");
                xe.Add(titles);
                foreach (int docID in this.DocTitles.Keys)
                {
                    titles.Add(new XElement(
                        "Title",
                        new XAttribute("DocID", docID.ToString()),
                        new XAttribute("Title", this.DocTitles[docID])));
                }
            }
            if (this.TermTermDistances != null && this.TermTermDistances.Count > 0)
            {
                XElement termDistances = new XElement("TermDistances");
                xe.Add(termDistances);
                foreach (int fromTermID in this.TermTermDistances.Keys)
                {
                    XElement fromTerm = new XElement("Term1", new XAttribute("TermID", fromTermID.ToString()));
                    foreach (int toTermID in this.TermTermDistances[fromTermID].Keys)
                    {
                        fromTerm.Add(
                            new XElement("Term2",
                                new XAttribute("TermID", toTermID.ToString()),
                                new XAttribute("Distance", this.TermTermDistances[fromTermID][toTermID])));
                    }
                }
            }

            return xe;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xe"></param>
        /// <returns></returns>
        public override ResultBase Instantiate(XElement xe)
        {
            DocumentClusteringOutput output = (DocumentClusteringOutput)base.Instantiate(xe);
            output.TaskName = xe.Attribute("TaskName").Value;
            foreach (XElement clusterLabel in xe.Elements("ClusterLabels").Elements("ClusterLabel"))
            {
                int clusterID = int.Parse(clusterLabel.Attribute("ClusterID").Value);
                string label = clusterLabel.Attribute("Label").Value;
                output.ClusterLabels.Add(clusterID, label);
            }
            foreach (XElement clusteredDoc in xe.Elements("ClusteredDocs").Elements("ClusteredDoc"))
            {
                int clusterID = int.Parse(clusteredDoc.Attribute("ClusterID").Value);
                List<int> docIDs = new List<int>();
                foreach (XElement doc in clusteredDoc.Elements("Doc"))
                {
                    int docID = int.Parse(doc.Attribute("DocID").Value);
                    docIDs.Add(docID);
                }
                output.ClusteredDocs.Add(clusterID, docIDs);
            }
            foreach (XElement clusterDiameter in xe.Elements("ClusterDiameters").Elements("ClusterDiameter"))
            {
                int clusterID = int.Parse(clusterDiameter.Attribute("ClusterID").Value);
                double diameter = double.Parse(clusterDiameter.Attribute("Diameter").Value);
                output.ClusterDiameters.Add(clusterID, diameter);
            }
            foreach (XElement docDocDistance in xe.Elements("DocDocDistances").Elements("DocDocDistance"))
            {
                int fromDocID = int.Parse(docDocDistance.Attribute("FromDocID").Value);
                int toDocID = int.Parse(docDocDistance.Attribute("ToDocID").Value);
                double distance = double.Parse(docDocDistance.Attribute("Distance").Value);
                if (output.DocDocDistances.ContainsKey(fromDocID))
                {
                    Dictionary<int, double> toDistances = output.DocDocDistances[fromDocID];
                    toDistances.Add(toDocID, distance);
                    output.DocDocDistances[fromDocID] = toDistances;
                }
                else
                {
                    Dictionary<int, double> toDistances = new Dictionary<int, double>();
                    toDistances.Add(toDocID, distance);
                    output.DocDocDistances.Add(fromDocID, toDistances);
                }
            }
            foreach (XElement term in xe.Elements("AllTerms").Elements("Term"))
            {
                int termID = int.Parse(term.Attribute("TermID").Value);
                string phrase = term.Attribute("Phrase").Value;
                output.AllTerms.Add(termID, phrase);
            }
            foreach (XElement doc in xe.Elements("DocTerms").Elements("Doc"))
            {
                int docID = int.Parse(doc.Attribute("DocID").Value);
                Dictionary<int, int> termFreqs = new Dictionary<int, int>();
                foreach (XElement termFreq in doc.Elements("DocTerm"))
                {
                    int termID = int.Parse(termFreq.Attribute("TermID").Value);
                    int freq = int.Parse(termFreq.Attribute("TermFreq").Value);
                    termFreqs.Add(termID, freq);
                }
                output.DocTermFreqs.Add(docID, termFreqs);
            }
            foreach (XElement termWeight in xe.Elements("TermWeights").Elements("TermWeight"))
            {
                int termID = int.Parse(termWeight.Attribute("TermID").Value);
                double weight = double.Parse(termWeight.Attribute("Weight").Value);
                output.TermWeights.Add(termID, weight);
            }
            foreach (XElement title in xe.Elements("Titles").Elements("Title"))
            {
                int docID = int.Parse(title.Attribute("DocID").Value);
                string docTitle = title.Attribute("Title").Value;
                output.DocTitles.Add(docID, docTitle);
            }
            foreach (XElement fromTerm in xe.Elements("TermDistances").Elements("Term1"))
            {
                int fromTermID = int.Parse(fromTerm.Attribute("TermID").Value);
                Dictionary<int, double> toTermDistances = new Dictionary<int, double>();
                foreach (XElement toTerm in fromTerm.Elements("Term2"))
                {
                    int toTermID = int.Parse(toTerm.Attribute("TermID").Value);
                    double distance = double.Parse(toTerm.Attribute("Distance").Value);
                    toTermDistances.Add(toTermID, distance);
                }
                output.TermTermDistances.Add(fromTermID, toTermDistances);
            }
            return output;
        }

        #endregion
    }
}
